﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using StockholmsStad.Data.Interfaces;
using StockholmsStad.Runtime.Serialization.DataContract;
using StockholmsStad.Runtime.Serialization.DataContract.Enums;
using StockholmsStad.Utilities.Exceptions;

namespace StockholmsStad.Business.Test
{
    /// <summary>
    ///This is a test class for BookingBusinessTest and is intended
    ///to contain all BookingBusinessTest Unit Tests
    ///</summary>
    [TestClass()]
    public class BookingBusinessTest
    {
        private readonly User _user = new User();
        private IDataContext _context;
        private MockRepository _mocks = new MockRepository();

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext { get; set; }

        #region Additional test attributes

        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        // Use TestInitialize to run code before running each test
        [TestInitialize()]
        public void MyTestInitialize()
        {
            _mocks = new MockRepository();
        }

        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //

        #endregion

        /// <summary>
        ///Remove old booking
        ///</summary>
        [TestMethod()]
        public void SaveBookingTest()
        {
            var agentFactory = new MockAgentFactory();
            var ticketAgent = _mocks.StrictMock<ITicketAgent>();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            var ceremonyAgent = _mocks.StrictMock<ICeremonyAgent>();
            var ministerAgent = _mocks.StrictMock<IMinisterAgent>();

            agentFactory.TicketAgent = ticketAgent;
            agentFactory.BookingAgent = bookingAgent;
            agentFactory.MinisterAgent = ministerAgent;
            agentFactory.CeremonyAgent = ceremonyAgent;

            BookingBusiness target = GetTarget(agentFactory);
            // Object
            var ticket = new Ticket()
            {
                BookingId = 1,
                TicketId = 4242,
                Couple = new Couple() { Person1 = new Person() { Email = "test@test.se" } }
            };
            Booking booking = null;
            // Expect
            Expect.Call(ticketAgent.GetTicket(_context, 4242)).Return(ticket);
            Expect.Call(() => bookingAgent.SendOrdinaryBookingMail(_context, ticket));
            Expect.Call(bookingAgent.GetBooking(_context, 1)).Return(booking = new Booking(){DateTime = null});
            Expect.Call(() => bookingAgent.RemoveBooking(_context, ticket.TicketId, "Test", "parvis@input.nu", "Test borttag"));
            //Expect.Call(() => ticketAgent.UpdateTicketBookingId(_context, ticket.TicketId, null));
            // Replay
            _mocks.ReplayAll();
            target.SaveBooking(4242, booking);
            _mocks.VerifyAll();
        }

        private BookingBusiness GetTarget(IAgentFactory agentFactory)
        {
            var target = new BookingBusiness(_user);
            target.AgentFactory = agentFactory;
            _context = _mocks.Stub<IDataContext>();
            IContextFactory contextFactory = new MockContextFactory(_context);
            //target.ContextFactory = contextFactory;
            return target;
        }

        /// <summary>
        /// No new, no old booking
        ///</summary>
        [TestMethod()]
        public void SaveBookingTest2()
        {
            var agentFactory = new MockAgentFactory();
            var ticketAgent = _mocks.StrictMock<ITicketAgent>();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            agentFactory.TicketAgent = ticketAgent;
            agentFactory.BookingAgent = bookingAgent;
            BookingBusiness target = GetTarget(agentFactory);
            // Object
            var ticket = new Ticket()
            {
                BookingId = null,
                TicketId = 4242,
                Couple = new Couple() { Person1 = new Person() { Email = "test@test.se" } }
            };
            const Booking booking = null;
            // Expect
            Expect.Call(ticketAgent.GetTicket(_context, 4242)).Return(ticket);
            Expect.Call(() => bookingAgent.SendOrdinaryBookingMail(_context, ticket));
            // Replay
            _mocks.ReplayAll();
            target.SaveBooking(4242, booking);
            _mocks.VerifyAll();
        }

        /// <summary>
        /// new booking, no old booking
        ///</summary>
        [TestMethod()]
        public void SaveBookingTest3()
        {
            var agentFactory = new MockAgentFactory();
            var ticketAgent = _mocks.StrictMock<ITicketAgent>();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            IMinisterAgent ministerAgent = _mocks.StrictMock<IMinisterAgent>();
            ICeremonyAgent ceremonyAgent = _mocks.StrictMock<ICeremonyAgent>();
            agentFactory.CeremonyAgent = ceremonyAgent;
            agentFactory.TicketAgent = ticketAgent;
            agentFactory.BookingAgent = bookingAgent;
            agentFactory.MinisterAgent = ministerAgent;
            BookingBusiness target = GetTarget(agentFactory);
            // Object
            var booking = new Booking()
                              {
                                  MinisterCode = "Abc123",
                                  CeremonyType = CeremonyType.Individual,
                                  DateTime = new TimeBlock() {StartDateTime = new DateTime(2008, 01, 01),},
                              };
            var ticket = new Ticket()
            {
                BookingId = null,
                TicketId = 4242,
                Ceremony = new Ceremony(),
                Couple = new Couple() { Person1 = new Person() { Email = "test@test.se" } }
            };
            const int ministerId = 5252;
            const int bookingId = 6262;
            // Expect
            Expect.Call(ticketAgent.GetTicket(_context, 4242)).Return(ticket);
            Expect.Call(() => ceremonyAgent.ResetCeremonyFlags(_context, 4242));
            Expect.Call(ministerAgent.GetMinisterIdByMinisterCode(_context, booking.MinisterCode)).Return(ministerId);
            Expect.Call(bookingAgent.AddBooking(_context, ticket.TicketId, booking, ministerId)).Return(bookingId);
            //Expect.Call(() => ticketAgent.UpdateTicketBookingId(_context, ticket.TicketId, 6262));
            // Replay
            _mocks.ReplayAll();
            target.SaveBooking(4242, booking);
            _mocks.VerifyAll();
        }

        /// <summary>
        /// new booking (with timeblock), existing old booking
        ///</summary>
        [TestMethod()]
        public void SaveBookingTest4()
        {
            var agentFactory = new MockAgentFactory();
            var ticketAgent = _mocks.StrictMock<ITicketAgent>();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            IMinisterAgent ministerAgent = _mocks.StrictMock<IMinisterAgent>();
            ICeremonyAgent ceremonyAgent = _mocks.StrictMock<ICeremonyAgent>();
            agentFactory.CeremonyAgent = ceremonyAgent;
            agentFactory.TicketAgent = ticketAgent;
            agentFactory.BookingAgent = bookingAgent;
            agentFactory.MinisterAgent = ministerAgent;
            BookingBusiness target = GetTarget(agentFactory);
            // Object
            var booking = new Booking()
                              {
                                  CeremonyType = CeremonyType.Ordinary,
                                  DateTime = new TimeBlock() {StartDateTime = new DateTime(2008, 01, 01),},
                              };
            const int bookingId = 6262;
            const int timeblockId = 7272;
            var ticket = new Ticket()
            {
                BookingId = 6262,
                TicketId = 4242,
                Ceremony = new Ceremony(),
                Couple = new Couple() { Person1 = new Person() { Email = "test@test.se" } }
            };
            var newBooking = new Booking()
            {
                CeremonyType = CeremonyType.Ordinary,
                DateTime = new TimeBlock() { StartDateTime = booking.DateTime.StartDateTime.AddDays(7) },
            };
            // Expect
            Expect.Call(ticketAgent.GetTicket(_context, 4242)).Return(ticket);
            Expect.Call(() => bookingAgent.SendOrdinaryBookingMail(_context, ticket));
            Expect.Call(bookingAgent.GetBooking(_context, 6262)).Return(booking);
            Expect.Call(() => bookingAgent.SendPreferredDateNotification(_context, booking.DateTime.StartDateTime));
            Expect.Call(() => ceremonyAgent.ResetCeremonyFlags(_context, 4242));
            //Expect.Call(bookingAgent.GetTimeBlockByStartDateTime(_context, newBooking.DateTime.StartDateTime)).Return(timeblockId);
            Expect.Call(() => bookingAgent.UpdateBooking(_context, ticket.TicketId, booking, 1, "TEST"));
            // Replay
            _mocks.ReplayAll();
            target.SaveBooking(4242, newBooking);
            _mocks.VerifyAll();
        }

        /// <summary>
        /// new booking (without timeblock), existing old booking
        ///</summary>
        [TestMethod()]
        public void SaveBookingTest5()
        {
            var agentFactory = new MockAgentFactory();
            var ticketAgent = _mocks.StrictMock<ITicketAgent>();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            IMinisterAgent ministerAgent = _mocks.StrictMock<IMinisterAgent>();
            ICeremonyAgent ceremonyAgent = _mocks.StrictMock<ICeremonyAgent>();
            agentFactory.CeremonyAgent = ceremonyAgent;
            agentFactory.TicketAgent = ticketAgent;
            agentFactory.BookingAgent = bookingAgent;
            agentFactory.MinisterAgent = ministerAgent;
            BookingBusiness target = GetTarget(agentFactory);
            // Object
            var booking = new Booking()
                              {
                                  CeremonyType = CeremonyType.Ordinary,
                                  PreferredDate1 = new DateTime(2008, 01, 01),
                              };
            const int bookingId = 6262;
            var ticket = new Ticket()
            {
                BookingId = 6262,
                TicketId = 4242,
                Ceremony = new Ceremony(),
                Couple = new Couple() { Person1 = new Person() { Email = "test@test.se" } }
            };
            // Expect
            Expect.Call(ticketAgent.GetTicket(_context, 4242)).Return(ticket);
            Expect.Call(() => bookingAgent.SendOrdinaryBookingMail(_context, ticket));
            Expect.Call(bookingAgent.GetBooking(_context, 6262)).Return(booking);
            Expect.Call(() => ceremonyAgent.ResetCeremonyFlags(_context, 4242));
            Expect.Call(() => bookingAgent.UpdateBooking(_context, ticket.TicketId, booking, null, "TEST"));
            // Replay
            _mocks.ReplayAll();
            target.SaveBooking(4242, booking);
            _mocks.VerifyAll();
        }

        /// <summary>
        ///A test for GetBooking
        ///</summary>
        [TestMethod()]
        public void GetBookingTest()
        {
            var agentFactory = new MockAgentFactory();
            var ticketAgent = _mocks.StrictMock<ITicketAgent>();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            IMinisterAgent ministerAgent = _mocks.StrictMock<IMinisterAgent>();
            agentFactory.TicketAgent = ticketAgent;
            agentFactory.BookingAgent = bookingAgent;
            agentFactory.MinisterAgent = ministerAgent;

            var booking = new Booking()
                              {
                                  CeremonyType = CeremonyType.Ordinary,
                                  PreferredDate1 =  new DateTime(2008, 01, 01),
                              };
            BookingBusiness target = GetTarget(agentFactory);
            const int bookingId = 6262;
            var ticket = new Ticket() {BookingId = 6262, TicketId = 4242};
            Expect.Call(ticketAgent.GetTicket(_context, 4242)).Return(ticket);
            // get booking
            Expect.Call(bookingAgent.GetBooking(_context, bookingId)).Return(booking);
            _mocks.ReplayAll();
            Booking actual = target.GetBooking(4242);
            Assert.AreEqual(booking, actual);
            _mocks.VerifyAll();
        }

        /// <summary>
        ///No existing booking
        ///</summary>
        [TestMethod()]
        public void GetBookingTest2()
        {
            var agentFactory = new MockAgentFactory();
            var ticketAgent = _mocks.StrictMock<ITicketAgent>();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            IMinisterAgent ministerAgent = _mocks.StrictMock<IMinisterAgent>();
            agentFactory.TicketAgent = ticketAgent;
            agentFactory.BookingAgent = bookingAgent;
            agentFactory.MinisterAgent = ministerAgent;

            const Booking booking = null;
            BookingBusiness target = GetTarget(agentFactory);
            var ticket = new Ticket() {BookingId = null, TicketId = 4242};
            Expect.Call(ticketAgent.GetTicket(_context, 4242)).Return(ticket);
            // get booking
            _mocks.ReplayAll();
            Booking actual = target.GetBooking(4242);
            Assert.AreEqual(booking, actual);
            _mocks.VerifyAll();
        }

        private MinisterTimeBlock CreateBookableTimeBlock(DateTime dateTime)
        {
            return new MinisterTimeBlock()
                       {
                           StartDateTime = dateTime,
                           TimeBlockStatus = TimeBlockStatus.Bookable,
                       };
        }

        [TestMethod()]
        public void GetBookedTimeBlockDates()
        {
            var agentFactory = new MockAgentFactory();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            agentFactory.BookingAgent = bookingAgent;

            BookingBusiness target = GetTarget(agentFactory);
            // Object
            DateTime date = DateTime.Now;
            IEnumerable<TimeBlock> timeblocks = new List<TimeBlock>
                                                    {
                                                        CreateBookableTimeBlock(new DateTime(2008, 01, 01, 12, 30, 0)),
                                                        CreateBookableTimeBlock(new DateTime(2008, 01, 02, 12, 30, 0)),
                                                        CreateBookableTimeBlock(new DateTime(2008, 01, 03, 12, 30, 0)),
                                                        CreateBookableTimeBlock(new DateTime(2008, 01, 04, 12, 30, 0)),
                                                        CreateBookableTimeBlock(new DateTime(2008, 01, 05, 12, 30, 0)),
                                                    };
            // Expect
            Func<IDataContext, DateTime, IEnumerable<TimeBlock>> compareArguments = (context, dateTime) =>
                                                                                          {
                                                                                              Assert.AreEqual(
                                                                                                  date.Year,
                                                                                                  dateTime.Year);
                                                                                              Assert.AreEqual(
                                                                                                  date.DayOfYear,
                                                                                                  dateTime.DayOfYear);
                                                                                              Assert.AreEqual(
                                                                                                  date.Hour,
                                                                                                  dateTime.Hour);
                                                                                              Assert.AreEqual(
                                                                                                  date.Minute,
                                                                                                  dateTime.Minute);
                                                                                              return timeblocks;
                                                                                          };
            Expect.Call(bookingAgent.GetBookedTimeBlockDates(_context, date)).IgnoreArguments().Do(compareArguments);
            // Replay
            _mocks.ReplayAll();
            IEnumerable<TimeBlock> actual = target.GetBookedTimeBlockDates();
            Assert.AreSame(timeblocks, actual);
            _mocks.VerifyAll();
        }

        [TestMethod()]
        public void GetFreeTimeBlockDateTimes()
        {
            var agentFactory = new MockAgentFactory();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            agentFactory.BookingAgent = bookingAgent;

            BookingBusiness target = GetTarget(agentFactory);
            // Object
            DateTime date = DateTime.Now;
            List<TimeBlock> timeblocks = new List<TimeBlock>
                                             {
                                                 CreateBookableTimeBlock(new DateTime(2008, 01, 01, 12, 30, 0)),
                                                 CreateBookableTimeBlock(new DateTime(2008, 01, 02, 12, 30, 0)),
                                                 CreateBookableTimeBlock(new DateTime(2008, 01, 03, 12, 30, 0)),
                                                 CreateBookableTimeBlock(new DateTime(2008, 01, 04, 12, 30, 0)),
                                                 CreateBookableTimeBlock(new DateTime(2008, 01, 05, 12, 30, 0)),
                                             };
            // Expect
            Func<IDataContext, DateTime, List<TimeBlock>> compareArguments = (context, dateTime) =>
                                                                                   {
                                                                                       Assert.AreEqual(date.Year,
                                                                                                       dateTime.Year);
                                                                                       Assert.AreEqual(date.DayOfYear,
                                                                                                       dateTime.
                                                                                                           DayOfYear);
                                                                                       Assert.AreEqual(date.Hour,
                                                                                                       dateTime.Hour);
                                                                                       Assert.AreEqual(date.Minute,
                                                                                                       dateTime.Minute);
                                                                                       return timeblocks;
                                                                                   };
            Expect.Call(bookingAgent.GetFreeTimeBlockDateTimes(_context, date)).IgnoreArguments().Do(compareArguments);
            // Replay
            _mocks.ReplayAll();
            IEnumerable<TimeBlock> actual = target.GetFreeTimeBlockDateTimes(true, true);
            Assert.AreSame(timeblocks, actual);
            _mocks.VerifyAll();
        }

        /// <summary>
        ///A test for BookingBusiness Constructor
        ///</summary>
        [TestMethod()]
        public void BookingBusinessConstructorTest()
        {
            const User user = null;
            var target = new BookingBusiness(user);
            Assert.IsNotNull(target);
        }

        /// <summary>
        /// new booking, inside period
        ///</summary>
        [TestMethod()]
        public void CheckImpedimentDocDatePeriodTest()
        {
            var agentFactory = new MockAgentFactory();
            var ticketAgent = _mocks.StrictMock<ITicketAgent>();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            IMinisterAgent ministerAgent = _mocks.StrictMock<IMinisterAgent>();
            IPropertiesAgent propertiesAgent = _mocks.StrictMock<IPropertiesAgent>();
            ICeremonyAgent ceremonyAgent = _mocks.StrictMock<ICeremonyAgent>();
            agentFactory.CeremonyAgent = ceremonyAgent;
            agentFactory.TicketAgent = ticketAgent;
            agentFactory.BookingAgent = bookingAgent;
            agentFactory.MinisterAgent = ministerAgent;
            agentFactory.PropertiesAgent = propertiesAgent;
            BookingBusiness target = GetTarget(agentFactory);
            // Object
            var booking = new Booking()
            {
                MinisterCode = "Abc123",
                CeremonyType = CeremonyType.Individual,
                DateTime = new TimeBlock() { StartDateTime = new DateTime(2008, 01, 01), },
            };
            var ticket = new Ticket() { BookingId = null, TicketId = 4242, Ceremony = new Ceremony() { ImpedimentDocIssuedDate = new DateTime(2008, 01, 01) },
            Couple = new Couple(){Person1 = new Person(){Email = "test@test.se"}}};
            const int ministerId = 5252;
            const int bookingId = 6262;
            // Expect
            Expect.Call(propertiesAgent.GetProperties(_context)).Return(new ParVisProperties() { ImpedimentDocValidMonths = 4 });
            Expect.Call(ticketAgent.GetTicket(_context, 4242)).Return(ticket);
            Expect.Call(() => ceremonyAgent.ResetCeremonyFlags(_context, 4242));
            Expect.Call(ministerAgent.GetMinisterIdByMinisterCode(_context, booking.MinisterCode)).Return(ministerId);
            Expect.Call(bookingAgent.AddBooking(_context, ticket.TicketId, booking, ministerId)).Return(bookingId);
            //Expect.Call(() => ticketAgent.UpdateTicketBookingId(_context, ticket.TicketId, 6262));
            // Replay
            _mocks.ReplayAll();
            target.SaveBooking(4242, booking);
            _mocks.VerifyAll();
        }

        /// <summary>
        /// new booking, before period
        ///</summary>
        [TestMethod()]
        [ExpectedException(typeof(StockholmStadSystemException))]
        public void CheckImpedimentDocDatePeriodTest2()
        {
            var agentFactory = new MockAgentFactory();
            var ticketAgent = _mocks.StrictMock<ITicketAgent>();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            IMinisterAgent ministerAgent = _mocks.StrictMock<IMinisterAgent>();
            IPropertiesAgent propertiesAgent = _mocks.StrictMock<IPropertiesAgent>();
            agentFactory.TicketAgent = ticketAgent;
            agentFactory.BookingAgent = bookingAgent;
            agentFactory.MinisterAgent = ministerAgent;
            agentFactory.PropertiesAgent = propertiesAgent;
            BookingBusiness target = GetTarget(agentFactory);
            // Object
            var booking = new Booking()
            {
                MinisterCode = "Abc123",
                CeremonyType = CeremonyType.Individual,
                DateTime = new TimeBlock() { StartDateTime = new DateTime(2008, 01, 01), },
            };
            var ticket = new Ticket()
            {
                BookingId = null,
                TicketId = 4242,
                Ceremony = new Ceremony() { ImpedimentDocIssuedDate = new DateTime(2008, 01, 02) },
                Couple = new Couple() { Person1 = new Person() { Email = "test@test.se" } }
            };
            // Expect
            Expect.Call(() => bookingAgent.SendOrdinaryBookingMail(_context, ticket));
            Expect.Call(propertiesAgent.GetProperties(_context)).Return(new ParVisProperties() { ImpedimentDocValidMonths = 4 });
            Expect.Call(ticketAgent.GetTicket(_context, 4242)).Return(ticket);
            // Replay
            _mocks.ReplayAll();
            target.SaveBooking(4242, booking);
            _mocks.VerifyAll();
        }

        /// <summary>
        /// new booking, after period
        ///</summary>
        [TestMethod()]
        [ExpectedException(typeof(StockholmStadSystemException))]
        public void CheckImpedimentDocDatePeriodTest3()
        {
            var agentFactory = new MockAgentFactory();
            var ticketAgent = _mocks.StrictMock<ITicketAgent>();
            var bookingAgent = _mocks.StrictMock<IBookingAgent>();
            IMinisterAgent ministerAgent = _mocks.StrictMock<IMinisterAgent>();
            IPropertiesAgent propertiesAgent = _mocks.StrictMock<IPropertiesAgent>();
            agentFactory.TicketAgent = ticketAgent;
            agentFactory.BookingAgent = bookingAgent;
            agentFactory.MinisterAgent = ministerAgent;
            agentFactory.PropertiesAgent = propertiesAgent;
            BookingBusiness target = GetTarget(agentFactory);
            // Object
            var booking = new Booking()
            {
                MinisterCode = "Abc123",
                CeremonyType = CeremonyType.Individual,
                DateTime = new TimeBlock() { StartDateTime = new DateTime(2008, 05, 01), },
            };
            var ticket = new Ticket()
            {
                BookingId = null,
                TicketId = 4242,
                Ceremony = new Ceremony() { ImpedimentDocIssuedDate = new DateTime(2008, 01, 01) },
                Couple = new Couple() { Person1 = new Person() { Email = "test@test.se" } }
            };
            // Expect
            Expect.Call(() => bookingAgent.SendOrdinaryBookingMail(_context, ticket));
            Expect.Call(propertiesAgent.GetProperties(_context)).Return(new ParVisProperties() { ImpedimentDocValidMonths = 4 });
            Expect.Call(ticketAgent.GetTicket(_context, 4242)).Return(ticket);
            // Replay
            _mocks.ReplayAll();
            target.SaveBooking(4242, booking);
            _mocks.VerifyAll();
        }

        [TestMethod()]
        public void CheckDatePeriodTest()
        {
            Assert.AreEqual(new DateTime(2008, 4, 30), Add4Months(new DateTime(2008, 1, 1)));
            Assert.AreEqual(new DateTime(2008, 5, 31), Add4Months(new DateTime(2008, 2, 1)));
            Assert.AreEqual(new DateTime(2008, 6, 14), Add4Months(new DateTime(2008, 2, 15)));
            Assert.AreEqual(new DateTime(2008, 6, 27), Add4Months(new DateTime(2008, 2, 28)));
            Assert.AreEqual(new DateTime(2008, 2, 29), Add4Months(new DateTime(2007, 10, 31)));
        }

        private static DateTime Add4Months(DateTime dateTime)
        {
            return dateTime.AddDays(-1).AddMonths(4);
        }
    }
}